#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <ctime>

using namespace std;

// 最大节点数，N + 1 (留出下标0作为空节点)
const int MAXN = 200005;

// 节点结构体
struct Node {
    int l, r;       // 左右子节点的数组下标 (代替指针)
    int val;        // 节点存储的值 (本题中为 1 到 N)
    int size;       // 以该节点为根的子树大小
    int rnd;        // 随机优先级 (维护堆性质)
    bool rev;       // 反转延迟标记 (Lazy Tag)
} tree[MAXN];

int root = 0;   // Treap 的根节点下标
int idx = 0;    // 节点分配计数器

// --- 辅助函数 ---

// 1. 获取子树大小
int get_size(int p) {
    return p ? tree[p].size : 0;
}

// 2. 信息更新 (更新子树大小)
void push_up(int p) {
    if (!p) return;
    tree[p].size = get_size(tree[p].l) + get_size(tree[p].r) + 1;
}

// 3. 标记下传 (Pushdown)
// 当需要访问或修改子节点时，必须先将父节点的标记下传
void push_down(int p) {
    if (!p || !tree[p].rev) return;

    // 交换左右子树 (结构上的反转)
    swap(tree[p].l, tree[p].r);
    
    // 向子节点传递反转标记
    if (tree[p].l) tree[tree[p].l].rev ^= 1;
    if (tree[p].r) tree[tree[p].r].rev ^= 1;
    
    // 清除当前节点的标记
    tree[p].rev = false;
}

// 4. 节点创建
int new_node(int v) {
    idx++;
    tree[idx].val = v;
    tree[idx].size = 1;
    tree[idx].rnd = rand(); // C++ rand() 简单实现，实际竞赛需更优随机数
    tree[idx].l = tree[idx].r = 0;
    tree[idx].rev = false;
    return idx;
}

// --- 核心操作 ---

// 5. 合并 (Merge) 操作
// 将 t1 和 t2 合并，要求 t1 中所有元素在中序遍历上都在 t2 之前
// 返回合并后的新树根下标
int merge(int t1, int t2) {
    if (!t1 || !t2) return t1 + t2; // 如果其中一个为空，返回另一个

    // 合并前，先下传标记
    push_down(t1);
    push_down(t2);

    // 比较优先级 (rnd)，优先级高的作为新根
    if (tree[t1].rnd > tree[t2].rnd) {
        // t1 作为根，t1的右子树与 t2 合并
        tree[t1].r = merge(tree[t1].r, t2);
        push_up(t1); // 更新 t1 的 size
        return t1;
    } else {
        // t2 作为根，t1 与 t2 的左子树合并
        tree[t2].l = merge(t1, tree[t2].l);
        push_up(t2); // 更新 t2 的 size
        return t2;
    }
}

// 6. 分裂 (Split) 操作 (按大小 k 分裂)
// 将 p 分裂成 t1 (前 k 个元素) 和 t2 (剩余元素)
// 注意：t1 和 t2 是通过引用传递的，会直接修改外部变量
void split(int p, int k, int &t1, int &t2) {
    if (!p) {
        t1 = t2 = 0;
        return;
    }
    
    // 分裂前，先下传标记
    push_down(p);

    int s_l = get_size(tree[p].l); // 左子树的大小

    if (k <= s_l) {
        // 当前节点 p 及其右子树都属于 t2，t1 只在 p 的左子树中
        split(tree[p].l, k, t1, tree[p].l); // 递归分裂左子树
        t2 = p; // p 成为 t2 的根 (已连接其左子树为 tree[p].l)
    } else {
        // 当前节点 p 及其左子树都属于 t1，t2 只在 p 的右子树中
        // k' = k - s_l - 1 是需要在右子树中寻找的元素个数
        split(tree[p].r, k - s_l - 1, tree[p].r, t2); // 递归分裂右子树
        t1 = p; // p 成为 t1 的根 (已连接其右子树为 tree[p].r)
    }
    
    push_up(p); // 更新 p 的 size
}


// --- 区间反转操作 ---

// 7. 区间反转 [l, r]
void reverse_interval(int l, int r) {
    int t1, t2, t3;
    
    // (1) 分裂出左侧部分 [1, l-1] -> t1
    // t1: [1, l-1] | t2: [l, N]
    split(root, l - 1, t1, t2);
    
    // (2) 分裂出中间部分 [l, r] -> t2
    // t2: [l, r] | t3: [r+1, N]
    split(t2, r - l + 1, t2, t3);
    
    // (3) 对中间部分打上反转标记
    if (t2) {
        tree[t2].rev ^= 1;
    }
    
    // (4) 合并回去
    // root = t1 | (t2 | t3)
    root = merge(t1, merge(t2, t3));
}

// --- 8. 中序遍历输出结果 ---
void print_inorder(int p) {
    if (!p) return;

    // 访问前，先下传标记，确保结构正确
    push_down(p); 

    print_inorder(tree[p].l);
    cout << tree[p].val << " ";
    print_inorder(tree[p].r);
}

// --- 主函数 ---

int main() {
    // 设置随机数种子
    srand(time(0)); 
    
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;
    cin >> n >> m;

    // 初始化序列: 1, 2, 3, ..., N
    for (int i = 1; i <= n; ++i) {
        // 依次将新节点合并到 Treap 的最右端
        root = merge(root, new_node(i));
    }

    // 执行 M 次操作
    for (int i = 0; i < m; ++i) {
        int l, r;
        cin >> l >> r;
        reverse_interval(l, r);
    }

    // 输出最终序列
    print_inorder(root);
    cout << endl;

    return 0;
}